This is an R Markdown Notebook. When you execute code within the notebook, the results appear beneath the code.
Try executing this chunk by clicking the Run button within the chunk or by placing your cursor inside it and pressing Ctrl+Shift+Enter.
library(xgboost)
载入程辑包:‘xgboost’
The following object is masked from ‘package:IRanges’:
slice
The following object is masked from ‘package:plotly’:
slice
The following object is masked from ‘package:dplyr’:
slice
library(Matrix)
载入程辑包:‘Matrix’
The following object is masked from ‘package:S4Vectors’:
expand
The following objects are masked from ‘package:tidyr’:
expand, pack, unpack
library(mclust)
__ ___________ __ _____________
/ |/ / ____/ / / / / / ___/_ __/
/ /|_/ / / / / / / / /\__ \ / /
/ / / / /___/ /___/ /_/ /___/ // /
/_/ /_/\____/_____/\____//____//_/ version 5.4.9
Type 'citation("mclust")' for citing this R package in publications.
ds0 <- readRDS("./ds0.rds")
ds1 <- readRDS("./ds1.rds")
ds2 <- readRDS("./ds2.rds")
分发训练集
Idents(ds2) <- ds2$conditions
ds2_AC <- subset(ds2, idents = "AC")
ds2_PA <- subset(ds2, idents = "PA")
ds2_AC <- ds2_AC %>% FindNeighbors(dims = 1:20) %>% FindClusters(resolution = 0.1)
Computing nearest neighbor graph
Computing SNN
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck
Number of nodes: 3061
Number of edges: 99234
Running Louvain algorithm...
0% 10 20 30 40 50 60 70 80 90 100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.9566
Number of communities: 4
Elapsed time: 0 seconds
ds2_PA <- ds2_PA %>% FindNeighbors(dims = 1:20) %>% FindClusters(resolution = 0.1)
Computing nearest neighbor graph
Computing SNN
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck
Number of nodes: 6498
Number of edges: 215869
Running Louvain algorithm...
0% 10 20 30 40 50 60 70 80 90 100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.9332
Number of communities: 3
Elapsed time: 0 seconds
umapplot(ds2_AC) + scale_y_continuous(limits = c(-5,15),breaks = NULL) +
scale_x_continuous(limits = c(-5,15),breaks = NULL)
Warning: Using `as.character()` on a quosure is deprecated as of rlang 0.3.0.
Please use `as_label()` or `as_name()` instead.
This warning is displayed once per session.
Scale for 'y' is already present. Adding another scale for 'y', which will replace the existing scale.
Scale for 'x' is already present. Adding another scale for 'x', which will replace the existing scale.
Warning: Removed 113 rows containing missing values (geom_point).

umapplot(ds2_PA)+ scale_y_continuous(limits = c(-5,15),breaks = NULL) +
scale_x_continuous(limits = c(-5,15),breaks = NULL)
Scale for 'y' is already present. Adding another scale for 'y', which will replace the existing scale.
Scale for 'x' is already present. Adding another scale for 'x', which will replace the existing scale.
Warning: Removed 19 rows containing missing values (geom_point).

# AC_markers <- FindAllMarkers(ds2_AC,logfc.threshold = 0.7, min.diff.pct = 0.2)
# PA_markers <- FindAllMarkers(ds2_PA,logfc.threshold = 0.7, min.diff.pct = 0.2)
在AC上预训练
AC_data <- get_data_table(ds2_AC, highvar = F, type = "data")
AC_label <- as.numeric(as.character(Idents(ds2_AC)))
set.seed(7)
index <- c(1:dim(AC_data)[2]) %>% sample(ceiling(0.3*dim(AC_data)[2]), replace = F, prob = NULL)
colnames(AC_data) <- NULL
AC_train_data <- list(data = t(as(AC_data[,-index],"dgCMatrix")), label = AC_label[-index])
AC_test_data <- list(data = t(as(AC_data[,index],"dgCMatrix")), label = AC_label[index])
# data(agaricus.train, package='xgboost')
AC_train <- xgb.DMatrix(data = AC_train_data$data,label = AC_train_data$label)
AC_test <- xgb.DMatrix(data = AC_test_data$data,label = AC_test_data$label)
# xgb_params_train = {
# 'objective':'multi:softprob',
# 'eval_metric':'mlogloss',
# 'num_class':self.numbertrainclasses,
# 'eta':0.2,
# 'max_depth':6,
# 'subsample': 0.6}
# nround = 200
watchlist <- list(train = AC_train, eval = AC_test)
xgb_param <- list(eta = 0.2, max_depth = 6,
subsample = 0.6, num_class = length(table(Idents(ds2_AC))),
objective = "multi:softmax", eval_metric = 'mlogloss')
bst_model <- xgb.train(xgb_param, AC_train, nrounds = 200, watchlist, verbose = 0)
xgboost_roc[["auc"]] #只需要这个值
Multi-class area under the curve: 0.9883
adjustedRandIndex(AC_test_data$label, predict_AC_test) #分类器性能
[1] 0.9585312
在PA上训练
PA_data <- get_data_table(ds2_PA, highvar = F, type = "data")
PA_label <- as.numeric(as.character(Idents(ds2_PA)))
set.seed(7)
index <- c(1:dim(PA_data)[2]) %>% sample(ceiling(0.3*dim(PA_data)[2]), replace = F, prob = NULL)
colnames(PA_data) <- NULL
PA_train_data <- list(data = t(as(PA_data[,-index],"dgCMatrix")), label = PA_label[-index])
PA_test_data <- list(data = t(as(PA_data[,index],"dgCMatrix")), label = PA_label[index])
PA_train <- xgb.DMatrix(data = PA_train_data$data,label = PA_train_data$label)
PA_test <- xgb.DMatrix(data = PA_test_data$data,label = PA_test_data$label)
watchlist <- list(train = PA_train, eval = PA_test)
xgb_param <- list(eta = 0.2, max_depth = 6,
subsample = 0.6, num_class = length(table(Idents(ds2_PA))),
objective = "multi:softmax", eval_metric = 'mlogloss')
bst_model <- xgb.train(xgb_param, PA_train, nrounds = 200, watchlist, verbose = 0)
xgboost_roc <- pROC::multiclass.roc(PA_test_data$label, predict_PA_test) #多分类ROC
Setting direction: controls < cases
Setting direction: controls < cases
Setting direction: controls < cases
xgboost_roc[["auc"]]
Multi-class area under the curve: 0.9635
adjustedRandIndex(PA_test_data$label, predict_PA_test) #PA分类器性能
[1] 0.8821278
选择特征common genes of top 500
使用所有来自PA的细胞训练分类器
应用在AC上,计算ARI
selected_features <- intersect(PA_genes$Feature, AC_genes$Feature)
write.csv(selected_features, "./datatable/selected_features.csv", row.names = F)
PA_data <- get_data_table(ds2_PA, highvar = F, type = "data")
PA_data <- PA_data[selected_features,]
PA_label <- as.numeric(as.character(Idents(ds2_PA)))
colnames(PA_data) <- NULL
PA_train_data <- list(data = t(as(PA_data,"dgCMatrix")), label = PA_label)
PA_train <- xgb.DMatrix(data = PA_train_data$data,label = PA_train_data$label)
xgb_param <- list(eta = 0.2, max_depth = 6,
subsample = 0.6, num_class = length(table(Idents(ds2_PA))),
objective = "multi:softmax", eval_metric = 'mlogloss')
bst_model <- xgb.train(xgb_param, PA_train, nrounds = 200, verbose = 1)
# 特征提取
importance <- xgb.importance(colnames(PA_train), model = bst_model)
head(importance)
xgb.ggplot.importance(head(importance,20),n_clusters = 1) + theme_bw()

write.csv(importance, "./datatable/PAtrain_features.csv", row.names = F)
# multi_featureplot(head(importance,9)$Feature, ds2)
应用到AC上
xgboost_roc <- pROC::multiclass.roc(AC_test_data$label, predict_AC_test) #多分类ROC
Setting direction: controls < cases
Setting direction: controls < cases
Setting direction: controls < cases
Setting direction: controls < cases
Setting direction: controls < cases
Setting direction: controls < cases
xgboost_roc[["auc"]]
Multi-class area under the curve: 0.8273
# multi_featureplot(head(importance,4)$Feature, ds2, split.by = "conditions")
# multi_featureplot(head(importance,9)$Feature, ds2_AC)
# 计算ARI
adjustedRandIndex(predict_AC_test, AC_test_data$label)
[1] 0.3024837
umapplot(ds2,split.by = "conditions")
table(ds2$conditions)
sankey plot
PA -> AC ARI 0.3024837
pre
true 0 1 2
0 0.980360065 0.003273322 0.016366612
1 0.799516908 0.196859903 0.003623188
2 0.453004622 0.493066256 0.053929122
3 0.002762431 0.052486188 0.944751381
AC -> PA ARI 0.1797689
pre
true 0 1 2 3
0 0.027107438 0.287272727 0.682644628 0.002975207
1 0.000349895 0.075227432 0.914975507 0.009447166
2 0.008130081 0.003252033 0.175609756 0.813008130
varify 部分
病变程度量化 # 数据集CA_dataset1 ## 在dataset2的AC上训练 使用top500 in AC, 7:3分发训练集
temp <- get_data_table(ds2_AC, highvar = F, type = "data")
AC_data <- matrix(data=0, nrow = length(AC_genes$Feature), ncol = length(colnames(temp)), byrow = FALSE, dimnames = list(AC_genes$Feature,colnames(temp)))
for(i in intersect(AC_genes$Feature, rownames(temp))){
AC_data[i,] <- temp[i,]
}
rm(temp)
AC_label <- as.numeric(as.character(Idents(ds2_AC)))
set.seed(7)
index <- c(1:dim(AC_data)[2]) %>% sample(ceiling(0.3*dim(AC_data)[2]), replace = F, prob = NULL)
colnames(AC_data) <- NULL
AC_train_data <- list(data = t(as(AC_data[,-index],"dgCMatrix")), label = AC_label[-index])
AC_test_data <- list(data = t(as(AC_data[,index],"dgCMatrix")), label = AC_label[index])
AC_train <- xgb.DMatrix(data = AC_train_data$data,label = AC_train_data$label)
AC_test <- xgb.DMatrix(data = AC_test_data$data,label = AC_test_data$label)
watchlist <- list(train = AC_train, eval = AC_test)
xgb_param <- list(eta = 0.2, max_depth = 6,
subsample = 0.6, num_class = length(table(Idents(ds2_AC))),
objective = "multi:softmax", eval_metric = 'mlogloss')
bst_model <- xgb.train(xgb_param, AC_train, nrounds = 200, watchlist, verbose = 0)
AC_confuse_matrix_test_prop
pre
true 0 1 2 3
0 0.994652406 0.000000000 0.005347594 0.000000000
1 0.000000000 0.987755102 0.012244898 0.000000000
2 0.000000000 0.021164021 0.968253968 0.010582011
3 0.000000000 0.000000000 0.045045045 0.954954955
#ROC曲线
xgboost_roc <- pROC::multiclass.roc(AC_test_data$label, predict_AC_test) #多分类ROC
Setting direction: controls < cases
Setting direction: controls < cases
Setting direction: controls < cases
Setting direction: controls < cases
Setting direction: controls < cases
Setting direction: controls < cases
xgboost_roc[["auc"]]
Multi-class area under the curve: 0.9913
# 计算ARI
adjustedRandIndex(predict_AC_test, AC_test_data$label)
[1] 0.9648073

temp <- get_data_table(ds1, highvar = F, type = "data")
ds1_data <- matrix(data=0, nrow = length(AC_genes$Feature), ncol = length(colnames(temp)), byrow = FALSE, dimnames = list(AC_genes$Feature,colnames(temp)))
for(i in intersect(AC_genes$Feature, rownames(temp))){
ds1_data[i,] <- temp[i,]
}
rm(temp)
ds1_label <- as.numeric(as.character(Idents(ds1)))
colnames(ds1_data) <- NULL
ds1_test_data <- list(data = t(as(ds1_data,"dgCMatrix")), label = ds1_label)
ds1_test <- xgb.DMatrix(data = ds1_test_data$data,label = ds1_test_data$label)
#计算混淆矩阵
predict_ds1_test <- round(predict(bst_model, newdata = ds1_test))
ds1_data_confuse_matrix_test <- table(ds1_test_data$label, predict_ds1_test, dnn=c("true","pre"))
ds1_data_confuse_matrix_test_prop <- prop.table(ds1_data_confuse_matrix_test,1)
x <- c("ds1_0", "ds1_1", "ds1_2", "ds1_3")
y <- c("AC_0", "AC_1", "AC_2", "AC_3")
prop <- as.numeric(ds1_data_confuse_matrix_test_prop)
data <- expand.grid(x = x, y = y) %>% bind_cols(prop = prop)
plot <- ggplot(data, aes(x = x, y = y, colour = prop, size = prop)) +
geom_point()+
scale_size_continuous(range = c(0, 10)) +
labs(x = "clusters", y = "inferred from") + theme_bw()
ggsave("./plots/ACmodel_dataset1.png", plot = plot, device = png, width = 5,height = 4)
ds1_data_confuse_matrix_test
pre
true 0 1 2 3
0 0 45 2516 8
1 4 94 787 7
2 0 6 474 10
3 0 1 50 347
ds1_data_confuse_matrix_test_prop #分析发育轨迹
pre
true 0 1 2 3
0 0.000000000 0.017516543 0.979369404 0.003114052
1 0.004484305 0.105381166 0.882286996 0.007847534
2 0.000000000 0.012244898 0.967346939 0.020408163
3 0.000000000 0.002512563 0.125628141 0.871859296
#ROC曲线
xgboost_roc <- pROC::multiclass.roc(ds1_test_data$label, predict_ds1_test) #多分类ROC
Setting direction: controls < cases
Setting direction: controls < cases
Setting direction: controls < cases
Setting direction: controls < cases
Setting direction: controls < cases
Setting direction: controls < cases
xgboost_roc[["auc"]]
Multi-class area under the curve: 0.7198
# 计算ARI
adjustedRandIndex(predict_ds1_test, ds1_test_data$label)
[1] 0.2321442
冠状动脉数据集
ds0 <- ds0 %>% FindNeighbors(dims = 1:20) %>% FindClusters(resolution = 0.1)
Computing nearest neighbor graph
Computing SNN
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck
Number of nodes: 5401
Number of edges: 173943
Running Louvain algorithm...
0% 10 20 30 40 50 60 70 80 90 100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.9544
Number of communities: 5
Elapsed time: 0 seconds
umapplot(ds0)

f("TAGLN",ds0)

# ds0_markers <- FindAllMarkers(ds0,logfc.threshold = 0.7, min.diff.pct = 0.2)
selected_features <- AC_genes$Feature
temp <- get_data_table(ds0, highvar = F, type = "data")
ds0_data <- matrix(data=0, nrow = length(selected_features),
ncol = length(colnames(temp)), byrow = FALSE,
dimnames = list(selected_features,colnames(temp)))
for(i in intersect(selected_features,rownames(temp))){
ds0_data[i,] <- temp[i,]
}
rm(temp)
ds0_label <- as.numeric(as.character(Idents(ds0)))
colnames(ds0_data) <- NULL
ds0_test_data <- list(data = t(as(ds0_data,"dgCMatrix")), label = ds0_label)
ds0_test <- xgb.DMatrix(data = ds0_test_data$data,label = ds0_test_data$label)
#计算混淆矩阵
predict_ds0_test <- round(predict(bst_model, newdata = ds0_test))
ds0_data_confuse_matrix_test <- table(ds0_test_data$label, predict_ds0_test, dnn=c("true","pre"))
ds0_data_confuse_matrix_test_prop <- prop.table(ds0_data_confuse_matrix_test,1)
x <- c("ds0_0", "ds0_1", "ds0_2", "ds0_3", "ds0_4")
y <- c("AC_0", "AC_1", "AC_2")
prop <- as.numeric(ds0_data_confuse_matrix_test_prop)
data <- expand.grid(x = x, y = y) %>% bind_cols(prop = prop)
plot <- ggplot(data, aes(x = x, y = y, colour = prop, size = prop)) +
geom_point()+
scale_size_continuous(range = c(0, 10)) +
labs(x = "clusters", y = "inferred from") + theme_bw()
ggsave("./plots/ACmodel_humancor.png", plot = plot, device = png, width = 5,height = 4)
ds0_data_confuse_matrix_test
pre
true 0 1 2
0 1997 2 179
1 98 156 1531
2 3 1208 2
3 173 0 0
4 52 0 0
ds0_data_confuse_matrix_test_prop #分析发育轨迹
pre
true 0 1 2
0 0.9168962351 0.0009182736 0.0821854913
1 0.0549019608 0.0873949580 0.8577030812
2 0.0024732069 0.9958779885 0.0016488046
3 1.0000000000 0.0000000000 0.0000000000
4 1.0000000000 0.0000000000 0.0000000000
#ROC曲线
xgboost_roc <- pROC::multiclass.roc(ds0_test_data$label, predict_ds0_test) #多分类ROC
Setting direction: controls < cases
Setting direction: controls < cases
Setting direction: controls < cases
Setting direction: controls < cases
Setting direction: controls > cases
Setting direction: controls > cases
Setting direction: controls > cases
Setting direction: controls > cases
Setting direction: controls > cases
Setting direction: controls < cases
# 计算ARI
adjustedRandIndex(predict_ds0_test, ds0_test_data$label)
[1] 0.7047121
labels <- lapply(levels(Idents(ds2_AC)), paste0, "_AC") %>% as.character()
labels2 <- lapply(levels(Idents(ds0)), paste0, "_ds0") %>% as.character()
sources <- rep(0:(length(labels)-1), each = length(labels2)) #注意这里的each和times的区别
colors <- rep(colors_list[1:length(labels)], each = length(labels2))
targets <- rep(length(labels)+0:(length(labels2)-1), times = length(labels))
plot_ly(type = "sankey", orientation = "h",
node = list(
label = c(labels,labels2),
color = colors_list, pad = 15, thickness = 30,
line = list(
color = "black",
width = 1)),
link = list(
source = sources,
target = targets,
value = as.numeric(ds0_data_confuse_matrix_test),
color = colors
))
# load("./init.RData")
multi_featureplot(head(importance2,9)$Feature, ds2_AC)
multi_featureplot(head(importance2,9)$Feature, ds0)
multi_featureplot(head(importance2,9)$Feature, ds1)
f("MYH11", ds2_AC)
umapplot(ds0)
淋巴细胞
Idents(lym_ds2) <- lym_ds2$conditions
lym_ds2_AC <- subset(lym_ds2, idents = "AC")
lym_ds2_PA <- subset(lym_ds2, idents = "PA")
lym_ds2_AC <- lym_ds2_AC %>% FindNeighbors(dims = 1:20) %>% FindClusters(resolution = 0.2)
Computing nearest neighbor graph
Computing SNN
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck
Number of nodes: 2990
Number of edges: 98189
Running Louvain algorithm...
0% 10 20 30 40 50 60 70 80 90 100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.9128
Number of communities: 5
Elapsed time: 0 seconds
umapplot(lym_ds2_AC)

lym_ds2_PA <- lym_ds2_PA %>% FindNeighbors(dims = 1:20) %>% FindClusters(resolution = 0.2)
Computing nearest neighbor graph
Computing SNN
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck
Number of nodes: 13746
Number of edges: 456548
Running Louvain algorithm...
0% 10 20 30 40 50 60 70 80 90 100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.9116
Number of communities: 6
Elapsed time: 1 seconds
umapplot(lym_ds2_PA)

用PA的lym训练
lym_PA_data <- get_data_table(lym_ds2_PA, highvar = F, type = "data")
lym_PA_label <- as.numeric(as.character(Idents(lym_ds2_PA)))
set.seed(7)
index <- c(1:dim(lym_PA_data)[2]) %>% sample(ceiling(0.3*dim(lym_PA_data)[2]), replace = F, prob = NULL)
colnames(lym_PA_data) <- NULL
lym_PA_train_data <- list(data = t(as(lym_PA_data[,-index],"dgCMatrix")), label = lym_PA_label[-index])
lym_PA_test_data <- list(data = t(as(lym_PA_data[,index],"dgCMatrix")), label = lym_PA_label[index])
lym_PA_train <- xgb.DMatrix(data = lym_PA_train_data$data,label = lym_PA_train_data$label)
lym_PA_test <- xgb.DMatrix(data = lym_PA_test_data$data,label = lym_PA_test_data$label)
watchlist <- list(train = lym_PA_train, eval = lym_PA_test)
xgb_param <- list(eta = 0.2, max_depth = 6,
subsample = 0.6, num_class = length(table(Idents(lym_ds2_PA))),
objective = "multi:softmax", eval_metric = 'mlogloss')
bst_model <- xgb.train(xgb_param, lym_PA_train, nrounds = 200, watchlist, verbose = 1)
# 特征提取
importance <- xgb.importance(colnames(lym_PA_train), model = bst_model)
head(importance)
xgb.ggplot.importance(head(importance,20),n_clusters = 1)+theme_bw() #这个cluster和分类没有关系
lym_PA_genes <- head(importance, 500) ##选择top500
write.csv(lym_PA_genes,"./datatable/lym_PA_features.csv", row.names = F)
#混淆矩阵
predict_lym_PA_test <- round(predict(bst_model, newdata = lym_PA_test))
lym_PA_confuse_matrix_test <- table(lym_PA_test_data$label, predict_lym_PA_test, dnn=c("true","pre"))
lym_PA_confuse_matrix_test_prop <- prop.table(lym_PA_confuse_matrix_test, 1)
lym_PA_confuse_matrix_test_prop
x <- c("PA_lym_0", "PA_lym_1", "PA_lym_2", "PA_lym_3", "PA_lym_4", "PA_lym_5")
y <- c("PA_lym_0", "PA_lym_1", "PA_lym_2", "PA_lym_3", "PA_lym_4", "PA_lym_5")
prop <- as.numeric(lym_PA_confuse_matrix_test_prop)
data <- expand.grid(x = x, y = y) %>% bind_cols(prop = prop)
plot <- ggplot(data, aes(x = x, y = y, colour = prop, size = prop)) +
geom_point()+
scale_size_continuous(range = c(0, 10)) +
labs(x = "clusters", y = "inferred from") + theme_bw()
ggsave("./plots/PAlymmodel.png", plot = plot, device = png, width = 7,height =6)
用AC的lym验证
xgboost_roc[["auc"]]
Multi-class area under the curve: 0.7198
adjustedRandIndex(predict_lym_AC_test, lym_AC_test_data$label)
[1] 0.7227654
lym_AC_confuse_matrix_test_prop
pre
true 0 1 2 3 4 5
0 0.794649882 0.004720692 0.000000000 0.200629426 0.000000000 0.000000000
1 0.000000000 0.030172414 0.897988506 0.000000000 0.071839080 0.000000000
2 0.130268199 0.829501916 0.032567050 0.003831418 0.000000000 0.003831418
3 0.000000000 0.041841004 0.000000000 0.000000000 0.958158996 0.000000000
4 0.000000000 0.000000000 0.000000000 0.000000000 0.000000000 1.000000000
labels <- lapply(levels(Idents(lym_ds2_PA)), paste0, "_lymPA") %>% as.character()
labels2 <- lapply(levels(Idents(lym_ds2_AC)), paste0, "_lymAC") %>% as.character()
sources <- rep(0:5, each = 5) #注意这里的each和times的区别
colors <- rep(colors_list[1:6], each = 5)
targets <- rep(6:10, times = 6)
plot_ly(type = "sankey", orientation = "h",
node = list(
label = c(labels,labels2),
color = colors_list, pad = 15, thickness = 30,
line = list(
color = "black",
width = 1)),
link = list(
source = sources,
target = targets,
value = as.numeric(lym_AC_confuse_matrix_test),
color = colors
))
umapplot(lym_ds2_AC)

umapplot(lym_ds2_PA)

Add a new chunk by clicking the Insert Chunk button on the toolbar or by pressing Ctrl+Alt+I.
When you save the notebook, an HTML file containing the code and output will be saved alongside it (click the Preview button or press Ctrl+Shift+K to preview the HTML file).
The preview shows you a rendered HTML copy of the contents of the editor. Consequently, unlike Knit, Preview does not run any R code chunks. Instead, the output of the chunk when it was last run in the editor is displayed.
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKVGhpcyBpcyBhbiBbUiBNYXJrZG93bl0oaHR0cDovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbSkgTm90ZWJvb2suIFdoZW4geW91IGV4ZWN1dGUgY29kZSB3aXRoaW4gdGhlIG5vdGVib29rLCB0aGUgcmVzdWx0cyBhcHBlYXIgYmVuZWF0aCB0aGUgY29kZS4gCgpUcnkgZXhlY3V0aW5nIHRoaXMgY2h1bmsgYnkgY2xpY2tpbmcgdGhlICpSdW4qIGJ1dHRvbiB3aXRoaW4gdGhlIGNodW5rIG9yIGJ5IHBsYWNpbmcgeW91ciBjdXJzb3IgaW5zaWRlIGl0IGFuZCBwcmVzc2luZyAqQ3RybCtTaGlmdCtFbnRlciouIAoKYGBge3J9CmxpYnJhcnkoeGdib29zdCkKbGlicmFyeShNYXRyaXgpCmxpYnJhcnkobWNsdXN0KQpsaWJyYXJ5KHRpZHl2ZXJzZSkKYGBgCgpgYGB7cn0KZHMwIDwtIHJlYWRSRFMoIi4vZHMwLnJkcyIpCmRzMSA8LSByZWFkUkRTKCIuL2RzMS5yZHMiKQpkczIgPC0gcmVhZFJEUygiLi9kczIucmRzIikKYGBgCgojIOWIhuWPkeiuree7g+mbhgpgYGB7cn0KSWRlbnRzKGRzMikgPC0gZHMyJGNvbmRpdGlvbnMKZHMyX0FDIDwtIHN1YnNldChkczIsIGlkZW50cyA9ICJBQyIpCmRzMl9QQSA8LSBzdWJzZXQoZHMyLCBpZGVudHMgPSAiUEEiKQpkczJfQUMgPC0gZHMyX0FDICU+JSBGaW5kTmVpZ2hib3JzKGRpbXMgPSAxOjIwKSAlPiUgRmluZENsdXN0ZXJzKHJlc29sdXRpb24gPSAwLjEpCmRzMl9QQSA8LSBkczJfUEEgJT4lIEZpbmROZWlnaGJvcnMoZGltcyA9IDE6MjApICU+JSBGaW5kQ2x1c3RlcnMocmVzb2x1dGlvbiA9IDAuMSkKdW1hcHBsb3QoZHMyX0FDKSArIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKC01LDE1KSxicmVha3MgPSBOVUxMKSArCiAgICAgICAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cyA9IGMoLTUsMTUpLGJyZWFrcyA9IE5VTEwpCnVtYXBwbG90KGRzMl9QQSkrIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKC01LDE1KSxicmVha3MgPSBOVUxMKSArCiAgICAgICAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cyA9IGMoLTUsMTUpLGJyZWFrcyA9IE5VTEwpCgojIEFDX21hcmtlcnMgPC0gRmluZEFsbE1hcmtlcnMoZHMyX0FDLGxvZ2ZjLnRocmVzaG9sZCA9IDAuNywgbWluLmRpZmYucGN0ID0gMC4yKQojIFBBX21hcmtlcnMgPC0gRmluZEFsbE1hcmtlcnMoZHMyX1BBLGxvZ2ZjLnRocmVzaG9sZCA9IDAuNywgbWluLmRpZmYucGN0ID0gMC4yKQpgYGAKCgoKCiMjIOWcqEFD5LiK6aKE6K6t57uDCmBgYHtyfQpBQ19kYXRhIDwtIGdldF9kYXRhX3RhYmxlKGRzMl9BQywgaGlnaHZhciA9IEYsIHR5cGUgPSAiZGF0YSIpCkFDX2xhYmVsIDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKElkZW50cyhkczJfQUMpKSkKCnNldC5zZWVkKDcpCmluZGV4IDwtIGMoMTpkaW0oQUNfZGF0YSlbMl0pICU+JSBzYW1wbGUoY2VpbGluZygwLjMqZGltKEFDX2RhdGEpWzJdKSwgcmVwbGFjZSA9IEYsIHByb2IgPSBOVUxMKQoKY29sbmFtZXMoQUNfZGF0YSkgPC0gTlVMTAoKQUNfdHJhaW5fZGF0YSA8LSBsaXN0KGRhdGEgPSB0KGFzKEFDX2RhdGFbLC1pbmRleF0sImRnQ01hdHJpeCIpKSwgbGFiZWwgPSBBQ19sYWJlbFstaW5kZXhdKQpBQ190ZXN0X2RhdGEgPC0gbGlzdChkYXRhID0gdChhcyhBQ19kYXRhWyxpbmRleF0sImRnQ01hdHJpeCIpKSwgbGFiZWwgPSBBQ19sYWJlbFtpbmRleF0pCgpBQ190cmFpbiA8LSB4Z2IuRE1hdHJpeChkYXRhID0gQUNfdHJhaW5fZGF0YSRkYXRhLGxhYmVsID0gQUNfdHJhaW5fZGF0YSRsYWJlbCkKQUNfdGVzdCA8LSB4Z2IuRE1hdHJpeChkYXRhID0gQUNfdGVzdF9kYXRhJGRhdGEsbGFiZWwgPSBBQ190ZXN0X2RhdGEkbGFiZWwpCgojIHhnYl9wYXJhbXNfdHJhaW4gPSB7CiMgICAgICdvYmplY3RpdmUnOidtdWx0aTpzb2Z0cHJvYicsCiMgICAgICdldmFsX21ldHJpYyc6J21sb2dsb3NzJywKIyAgICAgJ251bV9jbGFzcyc6c2VsZi5udW1iZXJ0cmFpbmNsYXNzZXMsCiMgICAgICdldGEnOjAuMiwKIyAgICAgJ21heF9kZXB0aCc6NiwKIyAgICAgJ3N1YnNhbXBsZSc6IDAuNn0KIyBucm91bmQgPSAyMDAKCndhdGNobGlzdCA8LSBsaXN0KHRyYWluID0gQUNfdHJhaW4sIGV2YWwgPSBBQ190ZXN0KQp4Z2JfcGFyYW0gPC0gbGlzdChldGEgPSAwLjIsIG1heF9kZXB0aCA9IDYsIAogICAgICAgICAgICAgICAgICBzdWJzYW1wbGUgPSAwLjYsICBudW1fY2xhc3MgPSBsZW5ndGgodGFibGUoSWRlbnRzKGRzMl9BQykpKSwKICAgICAgICAgICAgICAgICAgb2JqZWN0aXZlID0gIm11bHRpOnNvZnRtYXgiLCBldmFsX21ldHJpYyA9ICdtbG9nbG9zcycpCgpic3RfbW9kZWwgPC0geGdiLnRyYWluKHhnYl9wYXJhbSwgQUNfdHJhaW4sIG5yb3VuZHMgPSAyMDAsIHdhdGNobGlzdCwgdmVyYm9zZSA9IDApCmBgYAoKYGBge3IsZmlnLmhlaWdodD00LGZpZy53aWR0aD00fQojIOeJueW+geaPkOWPlgppbXBvcnRhbmNlIDwtIHhnYi5pbXBvcnRhbmNlKGNvbG5hbWVzKEFDX3RyYWluKSwgbW9kZWwgPSBic3RfbW9kZWwpCmhlYWQoaW1wb3J0YW5jZSkKeGdiLmdncGxvdC5pbXBvcnRhbmNlKGhlYWQoaW1wb3J0YW5jZSwyMCksbl9jbHVzdGVycyA9IDEpICsgdGhlbWVfbWluaW1hbCgpCgptdWx0aV9mZWF0dXJlcGxvdChoZWFkKGltcG9ydGFuY2UsOSkkRmVhdHVyZSxkczIpIApBQ19nZW5lcyA8LSBoZWFkKGltcG9ydGFuY2UsIDUwMCkgIyPpgInmi6l0b3A1MDAKCndyaXRlLmNzdihBQ19nZW5lcywgIi4vZGF0YXRhYmxlL0FDX2ZlYXR1cmVzLmNzdiIsIHJvdy5uYW1lcyA9IEYpCgoj5re35reG55+p6Zi1CnByZWRpY3RfQUNfdGVzdCA8LSByb3VuZChwcmVkaWN0KGJzdF9tb2RlbCwgbmV3ZGF0YSA9IEFDX3Rlc3QpKQoKQUNfY29uZnVzZV9tYXRyaXhfdGVzdCA8LSB0YWJsZShBQ190ZXN0X2RhdGEkbGFiZWwsIHByZWRpY3RfQUNfdGVzdCwgZG5uPWMoInRydWUiLCJwcmUiKSkKQUNfY29uZnVzZV9tYXRyaXhfdGVzdF9wcm9wIDwtIHByb3AudGFibGUoQUNfY29uZnVzZV9tYXRyaXhfdGVzdCwgMSkKQUNfY29uZnVzZV9tYXRyaXhfdGVzdF9wcm9wCgp4IDwtIGMoIkFDXzAiLCAiQUNfMSIsICJBQ18yIiwgIkFDXzMiKQp5IDwtIGMoIkFDXzAiLCAiQUNfMSIsICJBQ18yIiwgIkFDXzMiKQpwcm9wIDwtIGFzLm51bWVyaWMoQUNfY29uZnVzZV9tYXRyaXhfdGVzdF9wcm9wKQpkYXRhIDwtIGV4cGFuZC5ncmlkKHggPSB4LCB5ID0geSkgJT4lIGJpbmRfY29scyhwcm9wID0gcHJvcCkKcGxvdCA8LSBnZ3Bsb3QoZGF0YSwgYWVzKHggPSB4LCB5ID0geSwgY29sb3VyID0gcHJvcCwgc2l6ZSA9IHByb3ApKSArCiAgZ2VvbV9wb2ludCgpKwogIHNjYWxlX3NpemVfY29udGludW91cyhyYW5nZSA9IGMoMCwgMTApKSArICMgQWRqdXN0IGFzIHJlcXVpcmVkLgogIGxhYnMoeCA9IE5VTEwsIHkgPSBOVUxMKSArIHRoZW1lX2J3KCkKZ2dzYXZlKCIuL3Bsb3RzL0FDX3ByZXRyYWluLnBuZyIsIHBsb3QgPSBwbG90LCBkZXZpY2UgPSBwbmcsd2lkdGggPSA1LGhlaWdodCA9IDQpCgojUk9D5puy57q/CnhnYm9vc3Rfcm9jIDwtIHBST0M6Om11bHRpY2xhc3Mucm9jKEFDX3Rlc3RfZGF0YSRsYWJlbCwgcHJlZGljdF9BQ190ZXN0KSAj5aSa5YiG57G7Uk9DCnhnYm9vc3Rfcm9jW1siYXVjIl1dICPlj6rpnIDopoHov5nkuKrlgLwKYWRqdXN0ZWRSYW5kSW5kZXgoQUNfdGVzdF9kYXRhJGxhYmVsLCBwcmVkaWN0X0FDX3Rlc3QpICPliIbnsbvlmajmgKfog70KYGBgCgoKIyMg5ZyoUEHkuIrorq3nu4MKYGBge3J9ClBBX2RhdGEgPC0gZ2V0X2RhdGFfdGFibGUoZHMyX1BBLCBoaWdodmFyID0gRiwgdHlwZSA9ICJkYXRhIikKUEFfbGFiZWwgPC0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoSWRlbnRzKGRzMl9QQSkpKQpzZXQuc2VlZCg3KQppbmRleCA8LSBjKDE6ZGltKFBBX2RhdGEpWzJdKSAlPiUgc2FtcGxlKGNlaWxpbmcoMC4zKmRpbShQQV9kYXRhKVsyXSksIHJlcGxhY2UgPSBGLCBwcm9iID0gTlVMTCkKY29sbmFtZXMoUEFfZGF0YSkgPC0gTlVMTAoKUEFfdHJhaW5fZGF0YSA8LSBsaXN0KGRhdGEgPSB0KGFzKFBBX2RhdGFbLC1pbmRleF0sImRnQ01hdHJpeCIpKSwgbGFiZWwgPSBQQV9sYWJlbFstaW5kZXhdKQpQQV90ZXN0X2RhdGEgPC0gbGlzdChkYXRhID0gdChhcyhQQV9kYXRhWyxpbmRleF0sImRnQ01hdHJpeCIpKSwgbGFiZWwgPSBQQV9sYWJlbFtpbmRleF0pCgpQQV90cmFpbiA8LSB4Z2IuRE1hdHJpeChkYXRhID0gUEFfdHJhaW5fZGF0YSRkYXRhLGxhYmVsID0gUEFfdHJhaW5fZGF0YSRsYWJlbCkKUEFfdGVzdCA8LSB4Z2IuRE1hdHJpeChkYXRhID0gUEFfdGVzdF9kYXRhJGRhdGEsbGFiZWwgPSBQQV90ZXN0X2RhdGEkbGFiZWwpCgp3YXRjaGxpc3QgPC0gbGlzdCh0cmFpbiA9IFBBX3RyYWluLCBldmFsID0gUEFfdGVzdCkKeGdiX3BhcmFtIDwtIGxpc3QoZXRhID0gMC4yLCBtYXhfZGVwdGggPSA2LCAKICAgICAgICAgICAgICAgICAgc3Vic2FtcGxlID0gMC42LCAgbnVtX2NsYXNzID0gbGVuZ3RoKHRhYmxlKElkZW50cyhkczJfUEEpKSksCiAgICAgICAgICAgICAgICAgIG9iamVjdGl2ZSA9ICJtdWx0aTpzb2Z0bWF4IiwgZXZhbF9tZXRyaWMgPSAnbWxvZ2xvc3MnKQpic3RfbW9kZWwgPC0geGdiLnRyYWluKHhnYl9wYXJhbSwgUEFfdHJhaW4sIG5yb3VuZHMgPSAyMDAsIHdhdGNobGlzdCwgdmVyYm9zZSA9IDApCmBgYAoKYGBge3IsZmlnLmhlaWdodD00LGZpZy53aWR0aD00fQojIOeJueW+geaPkOWPlgppbXBvcnRhbmNlIDwtIHhnYi5pbXBvcnRhbmNlKGNvbG5hbWVzKFBBX3RyYWluKSwgbW9kZWwgPSBic3RfbW9kZWwpCmhlYWQoaW1wb3J0YW5jZSkKeGdiLmdncGxvdC5pbXBvcnRhbmNlKGhlYWQoaW1wb3J0YW5jZSwyMCksbl9jbHVzdGVycyA9IDEpICsgdGhlbWVfbWluaW1hbCgpCgptdWx0aV9mZWF0dXJlcGxvdChoZWFkKGltcG9ydGFuY2UsOSkkRmVhdHVyZSwgZHMyKQpQQV9nZW5lcyA8LSBoZWFkKGltcG9ydGFuY2UsIDUwMCkgIyPpgInmi6l0b3A1MDAKd3JpdGUuY3N2KFBBX2dlbmVzLCAiLi9kYXRhdGFibGUvUEFfZmVhdHVyZXMuY3N2Iiwgcm93Lm5hbWVzID0gRikKCiPmt7fmt4bnn6npmLUKcHJlZGljdF9QQV90ZXN0IDwtIHJvdW5kKHByZWRpY3QoYnN0X21vZGVsLCBuZXdkYXRhID0gUEFfdGVzdCkpCgpQQV9jb25mdXNlX21hdHJpeF90ZXN0IDwtIHRhYmxlKFBBX3Rlc3RfZGF0YSRsYWJlbCwgcHJlZGljdF9QQV90ZXN0LCBkbm49YygidHJ1ZSIsInByZSIpKQpQQV9jb25mdXNlX21hdHJpeF90ZXN0X3Byb3AgPC0gcHJvcC50YWJsZShQQV9jb25mdXNlX21hdHJpeF90ZXN0LDEpClBBX2NvbmZ1c2VfbWF0cml4X3Rlc3RfcHJvcAoKeCA8LSBjKCJQQV8wIiwgIlBBXzEiLCAiUEFfMiIpCnkgPC0gYygiUEFfMCIsICJQQV8xIiwgIlBBXzIiKQpwcm9wIDwtIGFzLm51bWVyaWMoUEFfY29uZnVzZV9tYXRyaXhfdGVzdF9wcm9wKQpkYXRhIDwtIGV4cGFuZC5ncmlkKHggPSB4LCB5ID0geSkgJT4lIGJpbmRfY29scyhwcm9wID0gcHJvcCkKcGxvdCA8LSBnZ3Bsb3QoZGF0YSwgYWVzKHggPSB4LCB5ID0geSwgY29sb3VyID0gcHJvcCwgc2l6ZSA9IHByb3ApKSArCiAgZ2VvbV9wb2ludCgpKwogIHNjYWxlX3NpemVfY29udGludW91cyhyYW5nZSA9IGMoMCwgMTApKSArICMgQWRqdXN0IGFzIHJlcXVpcmVkLgogIGxhYnMoeCA9IE5VTEwsIHkgPSBOVUxMKSArIHRoZW1lX2J3KCkKZ2dzYXZlKCIuL3Bsb3RzL1BBX3ByZXRyYWluLnBuZyIsIHBsb3QgPSBwbG90LCBkZXZpY2UgPSBwbmcsd2lkdGggPSA1LGhlaWdodCA9IDQpCgojUk9D5puy57q/Cgp4Z2Jvb3N0X3JvYyA8LSBwUk9DOjptdWx0aWNsYXNzLnJvYyhQQV90ZXN0X2RhdGEkbGFiZWwsIHByZWRpY3RfUEFfdGVzdCkgI+WkmuWIhuexu1JPQwp4Z2Jvb3N0X3JvY1tbImF1YyJdXQphZGp1c3RlZFJhbmRJbmRleChQQV90ZXN0X2RhdGEkbGFiZWwsIHByZWRpY3RfUEFfdGVzdCkgI1BB5YiG57G75Zmo5oCn6IO9CmBgYAojIyDpgInmi6nnibnlvoFjb21tb24gZ2VuZXMgb2YgdG9wIDUwMAojIyDkvb/nlKjmiYDmnInmnaXoh6pQQeeahOe7huiDnuiuree7g+WIhuexu+WZqAojIyDlupTnlKjlnKhBQ+S4iu+8jOiuoeeul0FSSQpgYGB7cixmaWcuaGVpZ2h0PTQsZmlnLndpZHRoPTR9CnNlbGVjdGVkX2ZlYXR1cmVzIDwtIGludGVyc2VjdChQQV9nZW5lcyRGZWF0dXJlLCBBQ19nZW5lcyRGZWF0dXJlKQp3cml0ZS5jc3Yoc2VsZWN0ZWRfZmVhdHVyZXMsICIuL2RhdGF0YWJsZS9zZWxlY3RlZF9mZWF0dXJlcy5jc3YiLCByb3cubmFtZXMgPSBGKQoKUEFfZGF0YSA8LSBnZXRfZGF0YV90YWJsZShkczJfUEEsIGhpZ2h2YXIgPSBGLCB0eXBlID0gImRhdGEiKQpQQV9kYXRhIDwtIFBBX2RhdGFbc2VsZWN0ZWRfZmVhdHVyZXMsXQpQQV9sYWJlbCA8LSBhcy5udW1lcmljKGFzLmNoYXJhY3RlcihJZGVudHMoZHMyX1BBKSkpCmNvbG5hbWVzKFBBX2RhdGEpIDwtIE5VTEwKClBBX3RyYWluX2RhdGEgPC0gbGlzdChkYXRhID0gdChhcyhQQV9kYXRhLCJkZ0NNYXRyaXgiKSksIGxhYmVsID0gUEFfbGFiZWwpClBBX3RyYWluIDwtIHhnYi5ETWF0cml4KGRhdGEgPSBQQV90cmFpbl9kYXRhJGRhdGEsbGFiZWwgPSBQQV90cmFpbl9kYXRhJGxhYmVsKQp4Z2JfcGFyYW0gPC0gbGlzdChldGEgPSAwLjIsIG1heF9kZXB0aCA9IDYsIAogICAgICAgICAgICAgICAgICBzdWJzYW1wbGUgPSAwLjYsICBudW1fY2xhc3MgPSBsZW5ndGgodGFibGUoSWRlbnRzKGRzMl9QQSkpKSwKICAgICAgICAgICAgICAgICAgb2JqZWN0aXZlID0gIm11bHRpOnNvZnRtYXgiLCBldmFsX21ldHJpYyA9ICdtbG9nbG9zcycpCgpic3RfbW9kZWwgPC0geGdiLnRyYWluKHhnYl9wYXJhbSwgUEFfdHJhaW4sIG5yb3VuZHMgPSAyMDAsIHZlcmJvc2UgPSAxKQoKIyDnibnlvoHmj5Dlj5YKaW1wb3J0YW5jZSA8LSB4Z2IuaW1wb3J0YW5jZShjb2xuYW1lcyhQQV90cmFpbiksIG1vZGVsID0gYnN0X21vZGVsKQpoZWFkKGltcG9ydGFuY2UpCnhnYi5nZ3Bsb3QuaW1wb3J0YW5jZShoZWFkKGltcG9ydGFuY2UsMjApLG5fY2x1c3RlcnMgPSAxKSArIHRoZW1lX2J3KCkKd3JpdGUuY3N2KGltcG9ydGFuY2UsICIuL2RhdGF0YWJsZS9QQXRyYWluX2ZlYXR1cmVzLmNzdiIsIHJvdy5uYW1lcyA9IEYpCgojIG11bHRpX2ZlYXR1cmVwbG90KGhlYWQoaW1wb3J0YW5jZSw5KSRGZWF0dXJlLCBkczIpCgpgYGAKIyMg5bqU55So5YiwQUPkuIoKYGBge3J9CkFDX2RhdGEgPC0gZ2V0X2RhdGFfdGFibGUoZHMyX0FDLCBoaWdodmFyID0gRiwgdHlwZSA9ICJkYXRhIikKQUNfZGF0YSA8LSBBQ19kYXRhW3NlbGVjdGVkX2ZlYXR1cmVzLF0KQUNfbGFiZWwgPC0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoSWRlbnRzKGRzMl9BQykpKQpjb2xuYW1lcyhBQ19kYXRhKSA8LSBOVUxMCkFDX3Rlc3RfZGF0YSA8LSBsaXN0KGRhdGEgPSB0KGFzKEFDX2RhdGEsImRnQ01hdHJpeCIpKSwgbGFiZWwgPSBBQ19sYWJlbCkKQUNfdGVzdCA8LSB4Z2IuRE1hdHJpeChkYXRhID0gQUNfdGVzdF9kYXRhJGRhdGEsbGFiZWwgPSBBQ190ZXN0X2RhdGEkbGFiZWwpCgoj6K6h566X5re35reG55+p6Zi1CnByZWRpY3RfQUNfdGVzdCA8LSByb3VuZChwcmVkaWN0KGJzdF9tb2RlbCwgbmV3ZGF0YSA9IEFDX3Rlc3QpKQoKQUNfY29uZnVzZV9tYXRyaXhfdGVzdCA8LSB0YWJsZShBQ190ZXN0X2RhdGEkbGFiZWwsIHByZWRpY3RfQUNfdGVzdCwgZG5uPWMoInRydWUiLCJwcmUiKSkKQUNfY29uZnVzZV9tYXRyaXhfdGVzdF9wcm9wIDwtIHByb3AudGFibGUoQUNfY29uZnVzZV9tYXRyaXhfdGVzdCwxKQpBQ19jb25mdXNlX21hdHJpeF90ZXN0X3Byb3AgICPliIbmnpDlj5HogrLovajov7kKCnggPC0gYygiQUNfMCIsICJBQ18xIiwgIkFDXzIiLCAiQUNfMyIpCnkgPC0gYygiUEFfMCIsICJQQV8xIiwgIlBBXzIiKQpwcm9wIDwtIGFzLm51bWVyaWMoQUNfY29uZnVzZV9tYXRyaXhfdGVzdF9wcm9wKQpkYXRhIDwtIGV4cGFuZC5ncmlkKHggPSB4LCB5ID0geSkgJT4lIGJpbmRfY29scyhwcm9wID0gcHJvcCkKcGxvdCA8LSBnZ3Bsb3QoZGF0YSwgYWVzKHggPSB4LCB5ID0geSwgY29sb3VyID0gcHJvcCwgc2l6ZSA9IHByb3ApKSArCiAgZ2VvbV9wb2ludCgpKwogIHNjYWxlX3NpemVfY29udGludW91cyhyYW5nZSA9IGMoMCwgMTApKSArIAogIGxhYnMoeCA9ICJjbHVzdGVycyIsIHkgPSAiaW5mZXJyZWQgZnJvbSIpICsgdGhlbWVfYncoKQoKZ2dzYXZlKCIuL3Bsb3RzL1BBdG9BQ190cmFpbi5wbmciLCBwbG90ID0gcGxvdCwgZGV2aWNlID0gcG5nLCB3aWR0aCA9IDUsaGVpZ2h0ID0gNCkKCiNST0Pmm7Lnur8KeGdib29zdF9yb2MgPC0gcFJPQzo6bXVsdGljbGFzcy5yb2MoQUNfdGVzdF9kYXRhJGxhYmVsLCBwcmVkaWN0X0FDX3Rlc3QpICPlpJrliIbnsbtST0MKeGdib29zdF9yb2NbWyJhdWMiXV0KCiMgbXVsdGlfZmVhdHVyZXBsb3QoaGVhZChpbXBvcnRhbmNlLDQpJEZlYXR1cmUsIGRzMiwgc3BsaXQuYnkgPSAiY29uZGl0aW9ucyIpCiMgbXVsdGlfZmVhdHVyZXBsb3QoaGVhZChpbXBvcnRhbmNlLDkpJEZlYXR1cmUsIGRzMl9BQykKCiMg6K6h566XQVJJIAphZGp1c3RlZFJhbmRJbmRleChwcmVkaWN0X0FDX3Rlc3QsIEFDX3Rlc3RfZGF0YSRsYWJlbCkKYGBgCgo8IS0tICMg6YCJ5oup54m55b6BY29tbW9uIGdlbmVzIG9mIHRvcCA1MDAgLS0+CjwhLS0gIyMg5L2/55So5omA5pyJ5p2l6IeqQUPnmoTnu4bog57orq3nu4PliIbnsbvlmaggLS0+Cgo8IS0tIGBgYHtyfSAtLT4KPCEtLSBBQ19kYXRhIDwtIGdldF9kYXRhX3RhYmxlKGRzMl9BQywgaGlnaHZhciA9IEYsIHR5cGUgPSAiZGF0YSIpIC0tPgo8IS0tIEFDX2RhdGEgPC0gQUNfZGF0YVtzZWxlY3RlZF9mZWF0dXJlcyxdIC0tPgo8IS0tIEFDX2xhYmVsIDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKElkZW50cyhkczJfQUMpKSkgLS0+CjwhLS0gY29sbmFtZXMoQUNfZGF0YSkgPC0gTlVMTCAtLT4KCjwhLS0gQUNfdHJhaW5fZGF0YSA8LSBsaXN0KGRhdGEgPSB0KGFzKEFDX2RhdGEsImRnQ01hdHJpeCIpKSwgbGFiZWwgPSBBQ19sYWJlbCkgLS0+Cgo8IS0tIEFDX3RyYWluIDwtIHhnYi5ETWF0cml4KGRhdGEgPSBBQ190cmFpbl9kYXRhJGRhdGEsbGFiZWwgPSBBQ190cmFpbl9kYXRhJGxhYmVsKSAtLT4KCjwhLS0geGdiX0FDcmFtIDwtIGxpc3QoZXRhID0gMC4yLCBtYXhfZGVwdGggPSA2LCAgLS0+CjwhLS0gICAgICAgICAgICAgICAgICAgc3Vic2FtcGxlID0gMC42LCAgbnVtX2NsYXNzID0gbGVuZ3RoKHRhYmxlKElkZW50cyhkczJfQUMpKSksIC0tPgo8IS0tICAgICAgICAgICAgICAgICAgIG9iamVjdGl2ZSA9ICJtdWx0aTpzb2Z0bWF4IiwgZXZhbF9tZXRyaWMgPSAnbWxvZ2xvc3MnKSAtLT4KCjwhLS0gYnN0X21vZGVsMiA8LSB4Z2IudHJhaW4oeGdiX0FDcmFtLCBBQ190cmFpbiwgbnJvdW5kcyA9IDIwMCwgdmVyYm9zZSA9IDEpIC0tPgoKPCEtLSAjIOeJueW+geaPkOWPliAtLT4KPCEtLSBpbXBvcnRhbmNlMiA8LSB4Z2IuaW1wb3J0YW5jZShjb2xuYW1lcyhBQ190cmFpbiksIG1vZGVsID0gYnN0X21vZGVsMikgLS0+CjwhLS0gaGVhZChpbXBvcnRhbmNlMikgLS0+CjwhLS0geGdiLmdncGxvdC5pbXBvcnRhbmNlKGhlYWQoaW1wb3J0YW5jZTIsMjApLG5fY2x1c3RlcnMgPSAxKSAtLT4KCjwhLS0gbXVsdGlfZmVhdHVyZXBsb3QoaGVhZChpbXBvcnRhbmNlMiw5KSRGZWF0dXJlLCBkczIpIC0tPgo8IS0tIGBgYCAtLT4KCgo8IS0tICMjIOW6lOeUqOWcqFBB5LiK77yM6K6h566XQVJJIC0tPgo8IS0tIGBgYHtyfSAtLT4KPCEtLSBQQV9kYXRhIDwtIGdldF9kYXRhX3RhYmxlKGRzMl9QQSwgaGlnaHZhciA9IEYsIHR5cGUgPSAiZGF0YSIpIC0tPgo8IS0tIFBBX2RhdGEgPC0gUEFfZGF0YVtzZWxlY3RlZF9mZWF0dXJlcyxdIC0tPgo8IS0tIFBBX2xhYmVsIDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKElkZW50cyhkczJfUEEpKSkgLS0+CjwhLS0gY29sbmFtZXMoUEFfZGF0YSkgPC0gTlVMTCAtLT4KCjwhLS0gUEFfdGVzdF9kYXRhIDwtIGxpc3QoZGF0YSA9IHQoYXMoUEFfZGF0YSwiZGdDTWF0cml4IikpLCBsYWJlbCA9IFBBX2xhYmVsKSAtLT4KCjwhLS0gUEFfdGVzdCA8LSB4Z2IuRE1hdHJpeChkYXRhID0gUEFfdGVzdF9kYXRhJGRhdGEsbGFiZWwgPSBQQV90ZXN0X2RhdGEkbGFiZWwpIC0tPgoKPCEtLSAj6K6h566X5re35reG55+p6Zi1IC0tPgo8IS0tIHByZWRpY3RfUEFfdGVzdCA8LSByb3VuZChwcmVkaWN0KGJzdF9tb2RlbDIsIG5ld2RhdGEgPSBQQV90ZXN0KSkgLS0+Cgo8IS0tIFBBX2NvbmZ1c2VfbWF0cml4X3Rlc3QgPC0gdGFibGUoUEFfdGVzdF9kYXRhJGxhYmVsLCBwcmVkaWN0X1BBX3Rlc3QsIGRubj1jKCJ0cnVlIiwicHJlIikpIC0tPgo8IS0tIFBBX2NvbmZ1c2VfbWF0cml4X3Rlc3RfcHJvcCA8LSBwcm9wLnRhYmxlKFBBX2NvbmZ1c2VfbWF0cml4X3Rlc3QsMSkgLS0+CjwhLS0gUEFfY29uZnVzZV9tYXRyaXhfdGVzdF9wcm9wICAj5YiG5p6Q5Y+R6IKy6L2o6L+5IC0tPgoKPCEtLSAjUk9D5puy57q/IC0tPgo8IS0tICMgeGdib29zdF9yb2MgPC0gcFJPQzo6bXVsdGljbGFzcy5yb2MoUEFfdGVzdF9kYXRhJGxhYmVsLCBwcmVkaWN0X1BBX3Rlc3QpICPlpJrliIbnsbtST0MgLS0+CjwhLS0geGdib29zdF9yb2MgPC0gcFJPQzo6cm9jKFBBX3Rlc3RfZGF0YSRsYWJlbCwgcHJlZGljdF9QQV90ZXN0KSAtLT4KPCEtLSBwbG90KHhnYm9vc3Rfcm9jLCBwcmludC5hdWM9VFJVRSwgYXVjLnBvbHlnb249VFJVRSwgIC0tPgo8IS0tICAgZ3JpZD1jKDAuMSwgMC4yKSxncmlkLmNvbD1jKCJncmVlbiIsICJyZWQiKSwgIC0tPgo8IS0tICAgbWF4LmF1Yy5wb2x5Z29uPVRSVUUsYXVjLnBvbHlnb24uY29sPSJza3libHVlIiwgIC0tPgo8IS0tICAgcHJpbnQudGhyZXM9VFJVRSxtYWluPSdST0MgY3VydmUnKSAj5YmN5Lik5Liq5YiG6YePUk9DIC0tPgoKPCEtLSAjIOiuoeeul0FSSSAgLS0+Cgo8IS0tIGFkanVzdGVkUmFuZEluZGV4KHByZWRpY3RfUEFfdGVzdCwgUEFfdGVzdF9kYXRhJGxhYmVsKSAtLT4KPCEtLSBgYGAgLS0+CgoKCmBgYHtyfQp1bWFwcGxvdChkczIsc3BsaXQuYnkgPSAiY29uZGl0aW9ucyIpCnRhYmxlKGRzMiRjb25kaXRpb25zKQpgYGAKCgojIHNhbmtleSBwbG90CgojIyBQQSAtPiBBQyAgICAgQVJJIDAuMzAyNDgzNwpgYGAKICAgIHByZQp0cnVlICAgICAgICAgICAwICAgICAgICAgICAxICAgICAgICAgICAyCiAgIDAgMC45ODAzNjAwNjUgMC4wMDMyNzMzMjIgMC4wMTYzNjY2MTIKICAgMSAwLjc5OTUxNjkwOCAwLjE5Njg1OTkwMyAwLjAwMzYyMzE4OAogICAyIDAuNDUzMDA0NjIyIDAuNDkzMDY2MjU2IDAuMDUzOTI5MTIyCiAgIDMgMC4wMDI3NjI0MzEgMC4wNTI0ODYxODggMC45NDQ3NTEzODEKYGBgCiMjIEFDIC0+IFBBICAgIEFSSSAwLjE3OTc2ODkKYGBgCiAgICBwcmUKdHJ1ZSAgICAgICAgICAgMCAgICAgICAgICAgMSAgICAgICAgICAgMiAgICAgICAgICAgMwogICAwIDAuMDI3MTA3NDM4IDAuMjg3MjcyNzI3IDAuNjgyNjQ0NjI4IDAuMDAyOTc1MjA3CiAgIDEgMC4wMDAzNDk4OTUgMC4wNzUyMjc0MzIgMC45MTQ5NzU1MDcgMC4wMDk0NDcxNjYKICAgMiAwLjAwODEzMDA4MSAwLjAwMzI1MjAzMyAwLjE3NTYwOTc1NiAwLjgxMzAwODEzMApgYGAKYGBge3J9CmxpYnJhcnkocGxvdGx5KQoKcGxvdF9seSh0eXBlID0gInNhbmtleSIsIG9yaWVudGF0aW9uID0gImgiLAogICAgbm9kZSA9IGxpc3QoCiAgICAgIGxhYmVsID0gYygiUEFfMCIsICJQQV8xIiwgIlBBXzIiLCAiQUNfMCIsIkFDXzEiLCJBQ18yIiwiQUNfMyIpLCAKICAgICAgY29sb3IgPSBjb2xvcnNfbGlzdCwgcGFkID0gMTUsIHRoaWNrbmVzcyA9IDMwLAogICAgICBsaW5lID0gbGlzdCgKICAgICAgICBjb2xvciA9ICJibGFjayIsCiAgICAgICAgd2lkdGggPSAxKSksCiAgICBsaW5rID0gbGlzdCgKICAgICAgc291cmNlID0gYygwLDAsMCwwLDEsMSwxLDEsMiwyLDIsMiksCiAgICAgIHRhcmdldCA9IGMoMyw0LDUsNiwzLDQsNSw2LDMsNCw1LDYpLAogICAgICB2YWx1ZSA9ICBhcy5udW1lcmljKEFDX2NvbmZ1c2VfbWF0cml4X3Rlc3QpLAogICAgICBjb2xvciA9IGMoIiM2NkMyQTUiLCIjNjZDMkE1IiwiIzY2QzJBNSIsIiM2NkMyQTUiLCIjRkM4RDYyIiwiI0ZDOEQ2MiIsIiNGQzhENjIiLAogICAgICAgICAgICAgICAgIiNGQzhENjIiLCIjOERBMENCIiwiIzhEQTBDQiIsIiM4REEwQ0IiLCIjOERBMENCIikKICAgICAgKSklPiUgbGF5b3V0KAogICAgdGl0bGUgPSAiUEEgLT4gQUMiLAogICAgZm9udCA9IGxpc3Qoc2l6ZSA9IDEwICkpCgp1bWFwcGxvdChkczJfQUMpCnVtYXBwbG90KGRzMl9QQSkKdW1hcHBsb3QoZHMyLHNwbGl0LmJ5ID0gImNvbmRpdGlvbnMiKQpgYGAKCiMgdmFyaWZ5IOmDqOWIhgrnl4Xlj5jnqIvluqbph4/ljJYKIyDmlbDmja7pm4ZDQV9kYXRhc2V0MQojIyDlnKhkYXRhc2V0MueahEFD5LiK6K6t57uDICDkvb/nlKh0b3A1MDAgaW4gQUMsIDc6M+WIhuWPkeiuree7g+mbhgpgYGB7cn0KdGVtcCA8LSBnZXRfZGF0YV90YWJsZShkczJfQUMsIGhpZ2h2YXIgPSBGLCB0eXBlID0gImRhdGEiKQpBQ19kYXRhIDwtIG1hdHJpeChkYXRhPTAsIG5yb3cgPSBsZW5ndGgoQUNfZ2VuZXMkRmVhdHVyZSksIG5jb2wgPSBsZW5ndGgoY29sbmFtZXModGVtcCkpLCBieXJvdyA9IEZBTFNFLCBkaW1uYW1lcyA9IGxpc3QoQUNfZ2VuZXMkRmVhdHVyZSxjb2xuYW1lcyh0ZW1wKSkpCgpmb3IoaSBpbiBpbnRlcnNlY3QoQUNfZ2VuZXMkRmVhdHVyZSwgcm93bmFtZXModGVtcCkpKXsKICBBQ19kYXRhW2ksXSA8LSB0ZW1wW2ksXQp9CnJtKHRlbXApCkFDX2xhYmVsIDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKElkZW50cyhkczJfQUMpKSkKc2V0LnNlZWQoNykKaW5kZXggPC0gYygxOmRpbShBQ19kYXRhKVsyXSkgJT4lIHNhbXBsZShjZWlsaW5nKDAuMypkaW0oQUNfZGF0YSlbMl0pLCByZXBsYWNlID0gRiwgcHJvYiA9IE5VTEwpCmNvbG5hbWVzKEFDX2RhdGEpIDwtIE5VTEwKCkFDX3RyYWluX2RhdGEgPC0gbGlzdChkYXRhID0gdChhcyhBQ19kYXRhWywtaW5kZXhdLCJkZ0NNYXRyaXgiKSksIGxhYmVsID0gQUNfbGFiZWxbLWluZGV4XSkKQUNfdGVzdF9kYXRhIDwtIGxpc3QoZGF0YSA9IHQoYXMoQUNfZGF0YVssaW5kZXhdLCJkZ0NNYXRyaXgiKSksIGxhYmVsID0gQUNfbGFiZWxbaW5kZXhdKQoKQUNfdHJhaW4gPC0geGdiLkRNYXRyaXgoZGF0YSA9IEFDX3RyYWluX2RhdGEkZGF0YSxsYWJlbCA9IEFDX3RyYWluX2RhdGEkbGFiZWwpCkFDX3Rlc3QgPC0geGdiLkRNYXRyaXgoZGF0YSA9IEFDX3Rlc3RfZGF0YSRkYXRhLGxhYmVsID0gQUNfdGVzdF9kYXRhJGxhYmVsKQoKd2F0Y2hsaXN0IDwtIGxpc3QodHJhaW4gPSBBQ190cmFpbiwgZXZhbCA9IEFDX3Rlc3QpCnhnYl9wYXJhbSA8LSBsaXN0KGV0YSA9IDAuMiwgbWF4X2RlcHRoID0gNiwgCiAgICAgICAgICAgICAgICAgIHN1YnNhbXBsZSA9IDAuNiwgIG51bV9jbGFzcyA9IGxlbmd0aCh0YWJsZShJZGVudHMoZHMyX0FDKSkpLAogICAgICAgICAgICAgICAgICBvYmplY3RpdmUgPSAibXVsdGk6c29mdG1heCIsIGV2YWxfbWV0cmljID0gJ21sb2dsb3NzJykKCmJzdF9tb2RlbCA8LSB4Z2IudHJhaW4oeGdiX3BhcmFtLCBBQ190cmFpbiwgbnJvdW5kcyA9IDIwMCwgd2F0Y2hsaXN0LCB2ZXJib3NlID0gMCkKYGBgCgpgYGB7cixmaWcud2lkdGg9NCxmaWcuaGVpZ2h0PTR9CiMg54m55b6B5o+Q5Y+WCmltcG9ydGFuY2UgPC0geGdiLmltcG9ydGFuY2UoY29sbmFtZXMoQUNfdHJhaW4pLCBtb2RlbCA9IGJzdF9tb2RlbCkKaGVhZChpbXBvcnRhbmNlKQp4Z2IuZ2dwbG90LmltcG9ydGFuY2UoaGVhZChpbXBvcnRhbmNlLDIwKSxuX2NsdXN0ZXJzID0gMSkgKyB0aGVtZV9idygpI+i/meS4qmNsdXN0ZXLlkozliIbnsbvmsqHmnInlhbPns7sKd3JpdGUuY3N2KGltcG9ydGFuY2UsICIuL2RhdGF0YWJsZS9BQ21vZGVsX2ZlYXR1cmVzLmNzdiIsIHJvdy5uYW1lcyA9IEYpCgoj5re35reG55+p6Zi1CnByZWRpY3RfQUNfdGVzdCA8LSByb3VuZChwcmVkaWN0KGJzdF9tb2RlbCwgbmV3ZGF0YSA9IEFDX3Rlc3QpKQoKQUNfY29uZnVzZV9tYXRyaXhfdGVzdCA8LSB0YWJsZShBQ190ZXN0X2RhdGEkbGFiZWwsIHByZWRpY3RfQUNfdGVzdCwgZG5uPWMoInRydWUiLCJwcmUiKSkKQUNfY29uZnVzZV9tYXRyaXhfdGVzdF9wcm9wIDwtIHByb3AudGFibGUoQUNfY29uZnVzZV9tYXRyaXhfdGVzdCwgMSkKCnggPC0gYygiQUNfMCIsICJBQ18xIiwgIkFDXzIiLCAiQUNfMyIpCnkgPC0gYygiQUNfMCIsICJBQ18xIiwgIkFDXzIiLCAiQUNfMyIpCnByb3AgPC0gYXMubnVtZXJpYyhBQ19jb25mdXNlX21hdHJpeF90ZXN0X3Byb3ApCmRhdGEgPC0gZXhwYW5kLmdyaWQoeCA9IHgsIHkgPSB5KSAlPiUgYmluZF9jb2xzKHByb3AgPSBwcm9wKQpwbG90IDwtIGdncGxvdChkYXRhLCBhZXMoeCA9IHgsIHkgPSB5LCBjb2xvdXIgPSBwcm9wLCBzaXplID0gcHJvcCkpICsKICBnZW9tX3BvaW50KCkrCiAgc2NhbGVfc2l6ZV9jb250aW51b3VzKHJhbmdlID0gYygwLCAxMCkpICsgCiAgbGFicyh4ID0gTlVMTCwgeSA9IE5VTEwpICsgdGhlbWVfYncoKQoKZ2dzYXZlKCIuL3Bsb3RzL0FDbW9kZWxfdHJhaW4ucG5nIiwgcGxvdCA9IHBsb3QsIGRldmljZSA9IHBuZywgd2lkdGggPSA1LGhlaWdodCA9IDQpCgpBQ19jb25mdXNlX21hdHJpeF90ZXN0X3Byb3AKI1JPQ+absue6vwp4Z2Jvb3N0X3JvYyA8LSBwUk9DOjptdWx0aWNsYXNzLnJvYyhBQ190ZXN0X2RhdGEkbGFiZWwsIHByZWRpY3RfQUNfdGVzdCkgI+WkmuWIhuexu1JPQwp4Z2Jvb3N0X3JvY1tbImF1YyJdXQoKIyDorqHnrpdBUkkgCmFkanVzdGVkUmFuZEluZGV4KHByZWRpY3RfQUNfdGVzdCwgQUNfdGVzdF9kYXRhJGxhYmVsKQpgYGAKCgoKYGBge3J9CmRzMSA8LSBkczEgJT4lIEZpbmROZWlnaGJvcnMoZGltcyA9IDE6MjApICU+JSBGaW5kQ2x1c3RlcnMocmVzb2x1dGlvbiA9IDAuMykKdW1hcHBsb3QoZHMxKQpkczEgPC0gUmVuYW1lSWRlbnRzKGRzMSwnMCcgPSAnMCcsJzEnID0nMCcsJzInPScxJywnMyc9JzInLCc0JyA9ICczJywnNScgPSAnMScpCnVtYXBwbG90KGRzMSkKZigiTFVNIixkczEpCmBgYAoKYGBge3J9CnRlbXAgPC0gZ2V0X2RhdGFfdGFibGUoZHMxLCBoaWdodmFyID0gRiwgdHlwZSA9ICJkYXRhIikKZHMxX2RhdGEgPC0gbWF0cml4KGRhdGE9MCwgbnJvdyA9IGxlbmd0aChBQ19nZW5lcyRGZWF0dXJlKSwgbmNvbCA9IGxlbmd0aChjb2xuYW1lcyh0ZW1wKSksIGJ5cm93ID0gRkFMU0UsIGRpbW5hbWVzID0gbGlzdChBQ19nZW5lcyRGZWF0dXJlLGNvbG5hbWVzKHRlbXApKSkKZm9yKGkgaW4gaW50ZXJzZWN0KEFDX2dlbmVzJEZlYXR1cmUsIHJvd25hbWVzKHRlbXApKSl7CiAgZHMxX2RhdGFbaSxdIDwtIHRlbXBbaSxdCn0Kcm0odGVtcCkKZHMxX2xhYmVsIDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKElkZW50cyhkczEpKSkKY29sbmFtZXMoZHMxX2RhdGEpIDwtIE5VTEwKZHMxX3Rlc3RfZGF0YSA8LSBsaXN0KGRhdGEgPSB0KGFzKGRzMV9kYXRhLCJkZ0NNYXRyaXgiKSksIGxhYmVsID0gZHMxX2xhYmVsKQpkczFfdGVzdCA8LSB4Z2IuRE1hdHJpeChkYXRhID0gZHMxX3Rlc3RfZGF0YSRkYXRhLGxhYmVsID0gZHMxX3Rlc3RfZGF0YSRsYWJlbCkKCiPorqHnrpfmt7fmt4bnn6npmLUKcHJlZGljdF9kczFfdGVzdCA8LSByb3VuZChwcmVkaWN0KGJzdF9tb2RlbCwgbmV3ZGF0YSA9IGRzMV90ZXN0KSkKCmRzMV9kYXRhX2NvbmZ1c2VfbWF0cml4X3Rlc3QgPC0gdGFibGUoZHMxX3Rlc3RfZGF0YSRsYWJlbCwgcHJlZGljdF9kczFfdGVzdCwgZG5uPWMoInRydWUiLCJwcmUiKSkKZHMxX2RhdGFfY29uZnVzZV9tYXRyaXhfdGVzdF9wcm9wIDwtIHByb3AudGFibGUoZHMxX2RhdGFfY29uZnVzZV9tYXRyaXhfdGVzdCwxKQoKeCA8LSBjKCJkczFfMCIsICJkczFfMSIsICJkczFfMiIsICJkczFfMyIpCnkgPC0gYygiQUNfMCIsICJBQ18xIiwgIkFDXzIiLCAiQUNfMyIpCgpwcm9wIDwtIGFzLm51bWVyaWMoZHMxX2RhdGFfY29uZnVzZV9tYXRyaXhfdGVzdF9wcm9wKQpkYXRhIDwtIGV4cGFuZC5ncmlkKHggPSB4LCB5ID0geSkgJT4lIGJpbmRfY29scyhwcm9wID0gcHJvcCkKcGxvdCA8LSBnZ3Bsb3QoZGF0YSwgYWVzKHggPSB4LCB5ID0geSwgY29sb3VyID0gcHJvcCwgc2l6ZSA9IHByb3ApKSArCiAgZ2VvbV9wb2ludCgpKwogIHNjYWxlX3NpemVfY29udGludW91cyhyYW5nZSA9IGMoMCwgMTApKSArIAogIGxhYnMoeCA9ICJjbHVzdGVycyIsIHkgPSAiaW5mZXJyZWQgZnJvbSIpICsgdGhlbWVfYncoKQoKZ2dzYXZlKCIuL3Bsb3RzL0FDbW9kZWxfZGF0YXNldDEucG5nIiwgcGxvdCA9IHBsb3QsIGRldmljZSA9IHBuZywgd2lkdGggPSA1LGhlaWdodCA9IDQpCgoKZHMxX2RhdGFfY29uZnVzZV9tYXRyaXhfdGVzdApkczFfZGF0YV9jb25mdXNlX21hdHJpeF90ZXN0X3Byb3AgICPliIbmnpDlj5HogrLovajov7kKI1JPQ+absue6vwp4Z2Jvb3N0X3JvYyA8LSBwUk9DOjptdWx0aWNsYXNzLnJvYyhkczFfdGVzdF9kYXRhJGxhYmVsLCBwcmVkaWN0X2RzMV90ZXN0KSAj5aSa5YiG57G7Uk9DCnhnYm9vc3Rfcm9jW1siYXVjIl1dCgojIOiuoeeul0FSSSAKYWRqdXN0ZWRSYW5kSW5kZXgocHJlZGljdF9kczFfdGVzdCwgZHMxX3Rlc3RfZGF0YSRsYWJlbCkKYGBgCgpgYGB7cn0KbGFiZWxzIDwtIGxhcHBseShsZXZlbHMoSWRlbnRzKGRzMl9BQykpLCBwYXN0ZTAsICJfQUMiKSAlPiUgYXMuY2hhcmFjdGVyKCkKbGFiZWxzMiA8LSBsYXBwbHkobGV2ZWxzKElkZW50cyhkczEpKSwgcGFzdGUwLCAiX2RzMSIpICU+JSBhcy5jaGFyYWN0ZXIoKQpzb3VyY2VzIDwtIHJlcCgwOihsZW5ndGgobGFiZWxzKS0xKSwgZWFjaCA9IGxlbmd0aChsYWJlbHMyKSkgICPms6jmhI/ov5nph4znmoRlYWNo5ZKMdGltZXPnmoTljLrliKsKY29sb3JzIDwtIHJlcChjb2xvcnNfbGlzdFsxOmxlbmd0aChsYWJlbHMpXSwgZWFjaCA9IGxlbmd0aChsYWJlbHMyKSkKdGFyZ2V0cyA8LSByZXAobGVuZ3RoKGxhYmVscykrMDoobGVuZ3RoKGxhYmVsczIpLTEpLCB0aW1lcyA9IGxlbmd0aChsYWJlbHMpKQoKcGxvdF9seSh0eXBlID0gInNhbmtleSIsIG9yaWVudGF0aW9uID0gImgiLAogICAgbm9kZSA9IGxpc3QoCiAgICAgIGxhYmVsID0gYyhsYWJlbHMsbGFiZWxzMiksIAogICAgICBjb2xvciA9IGNvbG9yc19saXN0LCBwYWQgPSAxNSwgdGhpY2tuZXNzID0gMzAsCiAgICAgIGxpbmUgPSBsaXN0KAogICAgICAgIGNvbG9yID0gImJsYWNrIiwKICAgICAgICB3aWR0aCA9IDEpKSwKICAgIGxpbmsgPSBsaXN0KAogICAgICBzb3VyY2UgPSBzb3VyY2VzLAogICAgICB0YXJnZXQgPSB0YXJnZXRzLAogICAgICB2YWx1ZSA9ICBhcy5udW1lcmljKGRzMV9kYXRhX2NvbmZ1c2VfbWF0cml4X3Rlc3QpLAogICAgICBjb2xvciA9IGNvbG9ycwogICAgICApKQojZHMx5qC35pys5pyA5YOPQUNfMueahOe7huiDnuS6mue+pApgYGAKCgoKIyDlhqDnirbliqjohInmlbDmja7pm4YKYGBge3J9CmRzMCA8LSBkczAgJT4lIEZpbmROZWlnaGJvcnMoZGltcyA9IDE6MjApICU+JSBGaW5kQ2x1c3RlcnMocmVzb2x1dGlvbiA9IDAuMSkKdW1hcHBsb3QoZHMwKQpmKCJUQUdMTiIsZHMwKQojIGRzMF9tYXJrZXJzIDwtIEZpbmRBbGxNYXJrZXJzKGRzMCxsb2dmYy50aHJlc2hvbGQgPSAwLjcsIG1pbi5kaWZmLnBjdCA9IDAuMikKYGBgCgpgYGB7cn0Kc2VsZWN0ZWRfZmVhdHVyZXMgPC0gQUNfZ2VuZXMkRmVhdHVyZQp0ZW1wIDwtIGdldF9kYXRhX3RhYmxlKGRzMCwgaGlnaHZhciA9IEYsIHR5cGUgPSAiZGF0YSIpCmRzMF9kYXRhIDwtIG1hdHJpeChkYXRhPTAsIG5yb3cgPSBsZW5ndGgoc2VsZWN0ZWRfZmVhdHVyZXMpLCAKICAgICAgICAgICAgICAgICAgIG5jb2wgPSBsZW5ndGgoY29sbmFtZXModGVtcCkpLCBieXJvdyA9IEZBTFNFLCAKICAgICAgICAgICAgICAgICAgIGRpbW5hbWVzID0gbGlzdChzZWxlY3RlZF9mZWF0dXJlcyxjb2xuYW1lcyh0ZW1wKSkpCmZvcihpIGluIGludGVyc2VjdChzZWxlY3RlZF9mZWF0dXJlcyxyb3duYW1lcyh0ZW1wKSkpewogIGRzMF9kYXRhW2ksXSA8LSB0ZW1wW2ksXQp9CnJtKHRlbXApCgpkczBfbGFiZWwgPC0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoSWRlbnRzKGRzMCkpKQpjb2xuYW1lcyhkczBfZGF0YSkgPC0gTlVMTApkczBfdGVzdF9kYXRhIDwtIGxpc3QoZGF0YSA9IHQoYXMoZHMwX2RhdGEsImRnQ01hdHJpeCIpKSwgbGFiZWwgPSBkczBfbGFiZWwpCmRzMF90ZXN0IDwtIHhnYi5ETWF0cml4KGRhdGEgPSBkczBfdGVzdF9kYXRhJGRhdGEsbGFiZWwgPSBkczBfdGVzdF9kYXRhJGxhYmVsKQoKI+iuoeeul+a3t+a3huefqemYtQpwcmVkaWN0X2RzMF90ZXN0IDwtIHJvdW5kKHByZWRpY3QoYnN0X21vZGVsLCBuZXdkYXRhID0gZHMwX3Rlc3QpKQoKZHMwX2RhdGFfY29uZnVzZV9tYXRyaXhfdGVzdCA8LSB0YWJsZShkczBfdGVzdF9kYXRhJGxhYmVsLCBwcmVkaWN0X2RzMF90ZXN0LCBkbm49YygidHJ1ZSIsInByZSIpKQpkczBfZGF0YV9jb25mdXNlX21hdHJpeF90ZXN0X3Byb3AgPC0gcHJvcC50YWJsZShkczBfZGF0YV9jb25mdXNlX21hdHJpeF90ZXN0LDEpCmBgYAoKCmBgYHtyfQp4IDwtIGMoImRzMF8wIiwgImRzMF8xIiwgImRzMF8yIiwgImRzMF8zIiwgImRzMF80IikKeSA8LSBjKCJBQ18wIiwgIkFDXzEiLCAiQUNfMiIpCgpwcm9wIDwtIGFzLm51bWVyaWMoZHMwX2RhdGFfY29uZnVzZV9tYXRyaXhfdGVzdF9wcm9wKQpkYXRhIDwtIGV4cGFuZC5ncmlkKHggPSB4LCB5ID0geSkgJT4lIGJpbmRfY29scyhwcm9wID0gcHJvcCkKcGxvdCA8LSBnZ3Bsb3QoZGF0YSwgYWVzKHggPSB4LCB5ID0geSwgY29sb3VyID0gcHJvcCwgc2l6ZSA9IHByb3ApKSArCiAgZ2VvbV9wb2ludCgpKwogIHNjYWxlX3NpemVfY29udGludW91cyhyYW5nZSA9IGMoMCwgMTApKSArIAogIGxhYnMoeCA9ICJjbHVzdGVycyIsIHkgPSAiaW5mZXJyZWQgZnJvbSIpICsgdGhlbWVfYncoKQpnZ3NhdmUoIi4vcGxvdHMvQUNtb2RlbF9odW1hbmNvci5wbmciLCBwbG90ID0gcGxvdCwgZGV2aWNlID0gcG5nLCB3aWR0aCA9IDUsaGVpZ2h0ID0gNCkKCmRzMF9kYXRhX2NvbmZ1c2VfbWF0cml4X3Rlc3QKZHMwX2RhdGFfY29uZnVzZV9tYXRyaXhfdGVzdF9wcm9wICAj5YiG5p6Q5Y+R6IKy6L2o6L+5CgojUk9D5puy57q/CnhnYm9vc3Rfcm9jIDwtIHBST0M6Om11bHRpY2xhc3Mucm9jKGRzMF90ZXN0X2RhdGEkbGFiZWwsIHByZWRpY3RfZHMwX3Rlc3QpICPlpJrliIbnsbtST0MKCiMg6K6h566XQVJJIAphZGp1c3RlZFJhbmRJbmRleChwcmVkaWN0X2RzMF90ZXN0LCBkczBfdGVzdF9kYXRhJGxhYmVsKQpgYGAKCgpgYGB7cn0KbGFiZWxzIDwtIGxhcHBseShsZXZlbHMoSWRlbnRzKGRzMl9BQykpLCBwYXN0ZTAsICJfQUMiKSAlPiUgYXMuY2hhcmFjdGVyKCkKbGFiZWxzMiA8LSBsYXBwbHkobGV2ZWxzKElkZW50cyhkczApKSwgcGFzdGUwLCAiX2RzMCIpICU+JSBhcy5jaGFyYWN0ZXIoKQpzb3VyY2VzIDwtIHJlcCgwOihsZW5ndGgobGFiZWxzKS0xKSwgZWFjaCA9IGxlbmd0aChsYWJlbHMyKSkgICPms6jmhI/ov5nph4znmoRlYWNo5ZKMdGltZXPnmoTljLrliKsKY29sb3JzIDwtIHJlcChjb2xvcnNfbGlzdFsxOmxlbmd0aChsYWJlbHMpXSwgZWFjaCA9IGxlbmd0aChsYWJlbHMyKSkKdGFyZ2V0cyA8LSByZXAobGVuZ3RoKGxhYmVscykrMDoobGVuZ3RoKGxhYmVsczIpLTEpLCB0aW1lcyA9IGxlbmd0aChsYWJlbHMpKQoKcGxvdF9seSh0eXBlID0gInNhbmtleSIsIG9yaWVudGF0aW9uID0gImgiLAogICAgbm9kZSA9IGxpc3QoCiAgICAgIGxhYmVsID0gYyhsYWJlbHMsbGFiZWxzMiksIAogICAgICBjb2xvciA9IGNvbG9yc19saXN0LCBwYWQgPSAxNSwgdGhpY2tuZXNzID0gMzAsCiAgICAgIGxpbmUgPSBsaXN0KAogICAgICAgIGNvbG9yID0gImJsYWNrIiwKICAgICAgICB3aWR0aCA9IDEpKSwKICAgIGxpbmsgPSBsaXN0KAogICAgICBzb3VyY2UgPSBzb3VyY2VzLAogICAgICB0YXJnZXQgPSB0YXJnZXRzLAogICAgICB2YWx1ZSA9ICBhcy5udW1lcmljKGRzMF9kYXRhX2NvbmZ1c2VfbWF0cml4X3Rlc3QpLAogICAgICBjb2xvciA9IGNvbG9ycwogICAgICApKQpgYGAKCgoKCmBgYHtyfQojIGxvYWQoIi4vaW5pdC5SRGF0YSIpCm11bHRpX2ZlYXR1cmVwbG90KGhlYWQoaW1wb3J0YW5jZTIsOSkkRmVhdHVyZSwgZHMyX0FDKQptdWx0aV9mZWF0dXJlcGxvdChoZWFkKGltcG9ydGFuY2UyLDkpJEZlYXR1cmUsIGRzMCkKbXVsdGlfZmVhdHVyZXBsb3QoaGVhZChpbXBvcnRhbmNlMiw5KSRGZWF0dXJlLCBkczEpCmYoIk1ZSDExIiwgZHMyX0FDKQp1bWFwcGxvdChkczApCmBgYAoKCiMg5reL5be057uG6IOeCgpgYGB7cn0KbHltX2RzMiA8LSBzdWJzZXQoQ0FfZGF0YXNldDIsIGlkZW50cyA9IGMoJzAnLCc0JywnOScpKQpJZGVudHMobHltX2RzMikgPC0gbHltX2RzMiRjb25kaXRpb25zCmx5bV9kczJfQUMgPC0gc3Vic2V0KGx5bV9kczIsIGlkZW50cyA9ICJBQyIpCmx5bV9kczJfUEEgPC0gc3Vic2V0KGx5bV9kczIsIGlkZW50cyA9ICJQQSIpCmx5bV9kczJfQUMgPC0gbHltX2RzMl9BQyAlPiUgRmluZE5laWdoYm9ycyhkaW1zID0gMToyMCkgJT4lIEZpbmRDbHVzdGVycyhyZXNvbHV0aW9uID0gMC4yKQp1bWFwcGxvdChseW1fZHMyX0FDKQpseW1fZHMyX1BBIDwtIGx5bV9kczJfUEEgJT4lIEZpbmROZWlnaGJvcnMoZGltcyA9IDE6MjApICU+JSBGaW5kQ2x1c3RlcnMocmVzb2x1dGlvbiA9IDAuMikKdW1hcHBsb3QobHltX2RzMl9QQSkKYGBgCgojIyDnlKhQQeeahGx5beiuree7gwpgYGB7cn0KbHltX1BBX2RhdGEgPC0gZ2V0X2RhdGFfdGFibGUobHltX2RzMl9QQSwgaGlnaHZhciA9IEYsIHR5cGUgPSAiZGF0YSIpCmx5bV9QQV9sYWJlbCA8LSBhcy5udW1lcmljKGFzLmNoYXJhY3RlcihJZGVudHMobHltX2RzMl9QQSkpKQoKc2V0LnNlZWQoNykKaW5kZXggPC0gYygxOmRpbShseW1fUEFfZGF0YSlbMl0pICU+JSBzYW1wbGUoY2VpbGluZygwLjMqZGltKGx5bV9QQV9kYXRhKVsyXSksIHJlcGxhY2UgPSBGLCBwcm9iID0gTlVMTCkKY29sbmFtZXMobHltX1BBX2RhdGEpIDwtIE5VTEwKbHltX1BBX3RyYWluX2RhdGEgPC0gbGlzdChkYXRhID0gdChhcyhseW1fUEFfZGF0YVssLWluZGV4XSwiZGdDTWF0cml4IikpLCBsYWJlbCA9IGx5bV9QQV9sYWJlbFstaW5kZXhdKQpseW1fUEFfdGVzdF9kYXRhIDwtIGxpc3QoZGF0YSA9IHQoYXMobHltX1BBX2RhdGFbLGluZGV4XSwiZGdDTWF0cml4IikpLCBsYWJlbCA9IGx5bV9QQV9sYWJlbFtpbmRleF0pCgpseW1fUEFfdHJhaW4gPC0geGdiLkRNYXRyaXgoZGF0YSA9IGx5bV9QQV90cmFpbl9kYXRhJGRhdGEsbGFiZWwgPSBseW1fUEFfdHJhaW5fZGF0YSRsYWJlbCkKbHltX1BBX3Rlc3QgPC0geGdiLkRNYXRyaXgoZGF0YSA9IGx5bV9QQV90ZXN0X2RhdGEkZGF0YSxsYWJlbCA9IGx5bV9QQV90ZXN0X2RhdGEkbGFiZWwpCgp3YXRjaGxpc3QgPC0gbGlzdCh0cmFpbiA9IGx5bV9QQV90cmFpbiwgZXZhbCA9IGx5bV9QQV90ZXN0KQp4Z2JfcGFyYW0gPC0gbGlzdChldGEgPSAwLjIsIG1heF9kZXB0aCA9IDYsIAogICAgICAgICAgICAgICAgICBzdWJzYW1wbGUgPSAwLjYsICBudW1fY2xhc3MgPSBsZW5ndGgodGFibGUoSWRlbnRzKGx5bV9kczJfUEEpKSksCiAgICAgICAgICAgICAgICAgIG9iamVjdGl2ZSA9ICJtdWx0aTpzb2Z0bWF4IiwgZXZhbF9tZXRyaWMgPSAnbWxvZ2xvc3MnKQoKYnN0X21vZGVsIDwtIHhnYi50cmFpbih4Z2JfcGFyYW0sIGx5bV9QQV90cmFpbiwgbnJvdW5kcyA9IDIwMCwgd2F0Y2hsaXN0LCB2ZXJib3NlID0gMSkKYGBgCgoKYGBge3J9CiMg54m55b6B5o+Q5Y+WCmltcG9ydGFuY2UgPC0geGdiLmltcG9ydGFuY2UoY29sbmFtZXMobHltX1BBX3RyYWluKSwgbW9kZWwgPSBic3RfbW9kZWwpCmhlYWQoaW1wb3J0YW5jZSkKeGdiLmdncGxvdC5pbXBvcnRhbmNlKGhlYWQoaW1wb3J0YW5jZSwyMCksbl9jbHVzdGVycyA9IDEpK3RoZW1lX2J3KCkgI+i/meS4qmNsdXN0ZXLlkozliIbnsbvmsqHmnInlhbPns7sKbHltX1BBX2dlbmVzIDwtIGhlYWQoaW1wb3J0YW5jZSwgNTAwKSAjI+mAieaLqXRvcDUwMAoKd3JpdGUuY3N2KGx5bV9QQV9nZW5lcywiLi9kYXRhdGFibGUvbHltX1BBX2ZlYXR1cmVzLmNzdiIsIHJvdy5uYW1lcyA9IEYpCiPmt7fmt4bnn6npmLUKcHJlZGljdF9seW1fUEFfdGVzdCA8LSByb3VuZChwcmVkaWN0KGJzdF9tb2RlbCwgbmV3ZGF0YSA9IGx5bV9QQV90ZXN0KSkKCmx5bV9QQV9jb25mdXNlX21hdHJpeF90ZXN0IDwtIHRhYmxlKGx5bV9QQV90ZXN0X2RhdGEkbGFiZWwsIHByZWRpY3RfbHltX1BBX3Rlc3QsIGRubj1jKCJ0cnVlIiwicHJlIikpCmx5bV9QQV9jb25mdXNlX21hdHJpeF90ZXN0X3Byb3AgPC0gcHJvcC50YWJsZShseW1fUEFfY29uZnVzZV9tYXRyaXhfdGVzdCwgMSkKbHltX1BBX2NvbmZ1c2VfbWF0cml4X3Rlc3RfcHJvcAoKeCA8LSBjKCJQQV9seW1fMCIsICJQQV9seW1fMSIsICJQQV9seW1fMiIsICJQQV9seW1fMyIsICJQQV9seW1fNCIsICJQQV9seW1fNSIpCnkgPC0gYygiUEFfbHltXzAiLCAiUEFfbHltXzEiLCAiUEFfbHltXzIiLCAiUEFfbHltXzMiLCAiUEFfbHltXzQiLCAiUEFfbHltXzUiKQoKcHJvcCA8LSBhcy5udW1lcmljKGx5bV9QQV9jb25mdXNlX21hdHJpeF90ZXN0X3Byb3ApCmRhdGEgPC0gZXhwYW5kLmdyaWQoeCA9IHgsIHkgPSB5KSAlPiUgYmluZF9jb2xzKHByb3AgPSBwcm9wKQpwbG90IDwtIGdncGxvdChkYXRhLCBhZXMoeCA9IHgsIHkgPSB5LCBjb2xvdXIgPSBwcm9wLCBzaXplID0gcHJvcCkpICsKICBnZW9tX3BvaW50KCkrCiAgc2NhbGVfc2l6ZV9jb250aW51b3VzKHJhbmdlID0gYygwLCAxMCkpICsgCiAgbGFicyh4ID0gImNsdXN0ZXJzIiwgeSA9ICJpbmZlcnJlZCBmcm9tIikgKyB0aGVtZV9idygpCmdnc2F2ZSgiLi9wbG90cy9QQWx5bW1vZGVsLnBuZyIsIHBsb3QgPSBwbG90LCBkZXZpY2UgPSBwbmcsIHdpZHRoID0gNyxoZWlnaHQgPTYpCmBgYAoKCiMjIOeUqEFD55qEbHlt6aqM6K+BCmBgYHtyfQpseW1fQUNfZGF0YSA8LSBnZXRfZGF0YV90YWJsZShseW1fZHMyX0FDLCBoaWdodmFyID0gRiwgdHlwZSA9ICJkYXRhIikKbHltX0FDX2xhYmVsIDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKElkZW50cyhseW1fZHMyX0FDKSkpCgpjb2xuYW1lcyhseW1fQUNfZGF0YSkgPC0gTlVMTAoKbHltX0FDX3Rlc3RfZGF0YSA8LSBsaXN0KGRhdGEgPSB0KGFzKGx5bV9BQ19kYXRhLCJkZ0NNYXRyaXgiKSksIGxhYmVsID0gbHltX0FDX2xhYmVsKQoKbHltX0FDX3Rlc3QgPC0geGdiLkRNYXRyaXgoZGF0YSA9IGx5bV9BQ190ZXN0X2RhdGEkZGF0YSxsYWJlbCA9IGx5bV9BQ190ZXN0X2RhdGEkbGFiZWwpCiMgbXVsdGlfZmVhdHVyZXBsb3QoaGVhZChpbXBvcnRhbmNlLDkpJEZlYXR1cmUsbHltX2RzMl9BQykKCiPmt7fmt4bnn6npmLUKcHJlZGljdF9seW1fQUNfdGVzdCA8LSByb3VuZChwcmVkaWN0KGJzdF9tb2RlbCwgbmV3ZGF0YSA9IGx5bV9BQ190ZXN0KSkKCmx5bV9BQ19jb25mdXNlX21hdHJpeF90ZXN0IDwtIHRhYmxlKGx5bV9BQ190ZXN0X2RhdGEkbGFiZWwsIHByZWRpY3RfbHltX0FDX3Rlc3QsIGRubj1jKCJ0cnVlIiwicHJlIikpCmx5bV9BQ19jb25mdXNlX21hdHJpeF90ZXN0X3Byb3AgPC0gcHJvcC50YWJsZShseW1fQUNfY29uZnVzZV9tYXRyaXhfdGVzdCwgMSkKbHltX0FDX2NvbmZ1c2VfbWF0cml4X3Rlc3RfcHJvcAoKCnggPC0gYygiUEFfbHltXzAiLCAiUEFfbHltXzEiLCAiUEFfbHltXzIiLCAiUEFfbHltXzMiLCAiUEFfbHltXzQiLCAiUEFfbHltXzUiKQp5IDwtIGMoIlBBX2x5bV8wIiwgIlBBX2x5bV8xIiwgIlBBX2x5bV8yIiwgIlBBX2x5bV8zIiwgIlBBX2x5bV80IikKCnByb3AgPC0gYXMubnVtZXJpYyhseW1fQUNfY29uZnVzZV9tYXRyaXhfdGVzdF9wcm9wKQpkYXRhIDwtIGV4cGFuZC5ncmlkKHggPSB4LCB5ID0geSkgJT4lIGJpbmRfY29scyhwcm9wID0gcHJvcCkKcGxvdCA8LSBnZ3Bsb3QoZGF0YSwgYWVzKHggPSB4LCB5ID0geSwgY29sb3VyID0gcHJvcCwgc2l6ZSA9IHByb3ApKSArCiAgZ2VvbV9wb2ludCgpKwogIHNjYWxlX3NpemVfY29udGludW91cyhyYW5nZSA9IGMoMCwgMTApKSArIAogIGxhYnMoeCA9ICJjbHVzdGVycyIsIHkgPSAiaW5mZXJyZWQgZnJvbSIpICsgdGhlbWVfYncoKQpnZ3NhdmUoIi4vcGxvdHMvUEFseW1tb2RlbF9BQy5wbmciLCBwbG90ID0gcGxvdCwgZGV2aWNlID0gcG5nLCB3aWR0aCA9IDcsaGVpZ2h0ID02KQp4Z2Jvb3N0X3JvY1tbImF1YyJdXQphZGp1c3RlZFJhbmRJbmRleChwcmVkaWN0X2x5bV9BQ190ZXN0LCBseW1fQUNfdGVzdF9kYXRhJGxhYmVsKQpseW1fQUNfY29uZnVzZV9tYXRyaXhfdGVzdF9wcm9wCmBgYAoKCmBgYHtyfQpsYWJlbHMgPC0gbGFwcGx5KGxldmVscyhJZGVudHMobHltX2RzMl9QQSkpLCBwYXN0ZTAsICJfbHltUEEiKSAlPiUgYXMuY2hhcmFjdGVyKCkKbGFiZWxzMiA8LSBsYXBwbHkobGV2ZWxzKElkZW50cyhseW1fZHMyX0FDKSksIHBhc3RlMCwgIl9seW1BQyIpICU+JSBhcy5jaGFyYWN0ZXIoKQpzb3VyY2VzIDwtIHJlcCgwOjUsIGVhY2ggPSA1KSAgI+azqOaEj+i/memHjOeahGVhY2jlkox0aW1lc+eahOWMuuWIqwpjb2xvcnMgPC0gcmVwKGNvbG9yc19saXN0WzE6Nl0sIGVhY2ggPSA1KQp0YXJnZXRzIDwtIHJlcCg2OjEwLCB0aW1lcyA9IDYpCgpwbG90X2x5KHR5cGUgPSAic2Fua2V5Iiwgb3JpZW50YXRpb24gPSAiaCIsCiAgICBub2RlID0gbGlzdCgKICAgICAgbGFiZWwgPSBjKGxhYmVscyxsYWJlbHMyKSwgCiAgICAgIGNvbG9yID0gY29sb3JzX2xpc3QsIHBhZCA9IDE1LCB0aGlja25lc3MgPSAzMCwKICAgICAgbGluZSA9IGxpc3QoCiAgICAgICAgY29sb3IgPSAiYmxhY2siLAogICAgICAgIHdpZHRoID0gMSkpLAogICAgbGluayA9IGxpc3QoCiAgICAgIHNvdXJjZSA9IHNvdXJjZXMsCiAgICAgIHRhcmdldCA9IHRhcmdldHMsCiAgICAgIHZhbHVlID0gIGFzLm51bWVyaWMobHltX0FDX2NvbmZ1c2VfbWF0cml4X3Rlc3QpLAogICAgICBjb2xvciA9IGNvbG9ycwogICAgICApKQoKCnVtYXBwbG90KGx5bV9kczJfQUMpCnVtYXBwbG90KGx5bV9kczJfUEEpCmBgYAoKQWRkIGEgbmV3IGNodW5rIGJ5IGNsaWNraW5nIHRoZSAqSW5zZXJ0IENodW5rKiBidXR0b24gb24gdGhlIHRvb2xiYXIgb3IgYnkgcHJlc3NpbmcgKkN0cmwrQWx0K0kqLgoKV2hlbiB5b3Ugc2F2ZSB0aGUgbm90ZWJvb2ssIGFuIEhUTUwgZmlsZSBjb250YWluaW5nIHRoZSBjb2RlIGFuZCBvdXRwdXQgd2lsbCBiZSBzYXZlZCBhbG9uZ3NpZGUgaXQgKGNsaWNrIHRoZSAqUHJldmlldyogYnV0dG9uIG9yIHByZXNzICpDdHJsK1NoaWZ0K0sqIHRvIHByZXZpZXcgdGhlIEhUTUwgZmlsZSkuCgpUaGUgcHJldmlldyBzaG93cyB5b3UgYSByZW5kZXJlZCBIVE1MIGNvcHkgb2YgdGhlIGNvbnRlbnRzIG9mIHRoZSBlZGl0b3IuIENvbnNlcXVlbnRseSwgdW5saWtlICpLbml0KiwgKlByZXZpZXcqIGRvZXMgbm90IHJ1biBhbnkgUiBjb2RlIGNodW5rcy4gSW5zdGVhZCwgdGhlIG91dHB1dCBvZiB0aGUgY2h1bmsgd2hlbiBpdCB3YXMgbGFzdCBydW4gaW4gdGhlIGVkaXRvciBpcyBkaXNwbGF5ZWQuCg==